Los datos originales utilizados se encuentran en el siguiente Link, los cuales corresponden a los datos de Aterrizajes y despegues en el año 2021 procesados por ANAC.
Leamos y analisemos que tiene este csv.
# datos.ANAC := datos del csv original de aterrizajes y vuelos
datos.ANAC <- read.csv('202109-informe-ministerio.csv', sep=';', encoding = 'utf-8')
datos.ANAC
colnames(datos.ANAC)
## [1] "Fecha" "Hora.UTC"
## [3] "Clase.de.Vuelo..todos.los.vuelos." "Clasificación.Vuelo"
## [5] "Tipo.de.Movimiento" "Aeropuerto"
## [7] "Origen...Destino" "Aerolinea.Nombre"
## [9] "Aeronave" "Pasajeros"
## [11] "Calidad.dato"
En este data.set tenemos \(11\) columnas Referidas a distintos vuelos, tanto internacionales como nacionales.
Quedemonos con los datos de los vuelos nacionales, esto es en los cuales la clasificacion de vuelos es Dom. Para esto nos apollaremos en la libreria Tidyverse
require(tidyverse)
## Loading required package: tidyverse
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.5 v purrr 0.3.4
## v tibble 3.1.3 v dplyr 1.0.7
## v tidyr 1.1.3 v stringr 1.4.0
## v readr 2.0.0 v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
# Modificamos un poco los nombres por comodidad
datos.ANAC <- datos.ANAC %>%
rename(
Clase_Vuelo = Clase.de.Vuelo..todos.los.vuelos.,
Clasificacion_Vuelo = `Clasificación.Vuelo`,
)
colnames(datos.ANAC)
## [1] "Fecha" "Hora.UTC" "Clase_Vuelo"
## [4] "Clasificacion_Vuelo" "Tipo.de.Movimiento" "Aeropuerto"
## [7] "Origen...Destino" "Aerolinea.Nombre" "Aeronave"
## [10] "Pasajeros" "Calidad.dato"
datos.ANAC
Ahora si, busquemos todos los vuelos dentro de argentina.
# datos.vuelos.nacionales := datos del csv de ANAC correspondiente a los vuelos nacionales
datos.vuelos.nacionales <- datos.ANAC %>%
filter(Clasificacion_Vuelo == "Dom", Clase_Vuelo == "Regular")
datos.vuelos.nacionales
Una de las cosas que podemos notar, es que El Tipo de Movimiento se vincula con un Aeropuerto (dato en IATA) al mismo tiempo este se vincula con un dato de un Aeropuerto tambien en IATA en la Columna de Origen...Destino, esto debe interpretarse como:
El avión despega de el Aeropuerto \(x\) con Destino \(y\)
o bien
El avión aterriza Al Aeropuerto \(x\) con Origen \(y\)
Esto es algo que debemos tener en cuenta más adelante para poder organizar bien los datos.
Entonces, teniendo esto en cuenta, creemos dentro de este data.set dos columnas nuevas una señalando cual es el origen y el destino del vuelo, y asi ya tener bien identificado cual sera el viaje recorrido por el avion.
datos.vuelos.nacionales <-datos.vuelos.nacionales %>%
mutate(Origen =
if_else(Tipo.de.Movimiento=='Despegue',Aeropuerto,Origen...Destino),
Destino =
if_else(Tipo.de.Movimiento=='Despegue',Origen...Destino,Aeropuerto))
datos.vuelos.nacionales
En la consigna del tp, nos dejan el siguiente Link. Este tiene una entrada de wikipedia el cual posee una tabla con datos de aeropuertos nacionales.
library(httr)
library(rvest)
##
## Attaching package: 'rvest'
## The following object is masked from 'package:readr':
##
## guess_encoding
pagina.wiki <- read_html('https://en.wikipedia.org/wiki/List_of_airports_in_Argentina')
elemento_tabla <- html_element(pagina.wiki,'.wikitable')
datos.wiki <- html_table(elemento_tabla)
# datos.wiki := datos correspondientes a la tabla de wikipedia (sin ningun filtro)
datos.wiki
Nosotros necesitamos los datos de IATA en lugar de los de ICAO, pues, en el dataset en ANAC los datos de los Aeropuertos utilizan los codigos en IATA.
Ahora, podemos ver directamente en la tabla de wikipedia, que hay muchos aeropuertos que no estan identificados mediante este codigo por lo que necesitamos filtrarlos
codigos <- unique(datos.wiki$IATA)
codigos <- codigos[codigos!=""] #elimino el string vacio
length(codigos)
## [1] 105
codigos
## [1] "ARR" "BHI" "BRC" "AEP" "EPA" "EZE" "CPG" "CTC" "CCT" "CVH" "CRR" "CNT"
## [13] "HOS" "CLX" "CRD" "COC" "COR" "CSZ" "CNQ" "CUT" "UZU" "EHL" "FTE" "ING"
## [25] "EMX" "ELO" "EQS" "FMA" "GPO" "GNR" "VGS" "GGS" "OYA" "GHU" "IGB" "JSM"
## [37] "JUJ" "JNI" "LCM" "LPG" "IRJ" "LHS" "LLS" "LCP" "LMD" "LGS" "MQD" "MDQ"
## [49] "MDZ" "MDX" "RLO" "MJR" "MCS" "NEC" "NQN" "OVR" "ORA" "PRA" "AOL" "PEH"
## [61] "PMQ" "PSS" "PRQ" "PUD" "IGR" "PMY" "ULA" "RZA" "RAF" "RCQ" "RES" "RDS"
## [73] "RCU" "RGL" "RGA" "ROY" "RYO" "ROS" "SLA" "OES" "UAQ" "LUQ" "CPC" "AFA"
## [85] "SFN" "RSA" "SST" "SDE" "OLN" "SGV" "NCJ" "TDL" "TTG" "RHD" "REL" "OYO"
## [97] "TUC" "USH" "VCF" "VDM" "VDR" "VLG" "VMR" "VME" "APZ"
Otro dato que necesitaremos limpiar es el dato de las coordenadas. Cada coordenada en la tabla de datos.wiki esta como un objeto string, el problema es que renemos los mismos datos en formatos diferentes y solo nos interesa quedarnos con los datos que son puramente numericos, pues, luego queremos utilizarlos para calcular la distancia entre Aeropuertos.
Tratemos de filtrar los datos de coordenadas y separar latitudes y longitudes.
# Inicializamos algunas variables
latitud <- c()
longitud <- c()
primer.filtro <- c()
segundo.filtro <- c()
coordenadas.filtradas <- c()
# Tomamos la columna de coordenadas desde la tabla de wikipedia
coordenadas <- datos.wiki$Coordinates
# Separamos las coordenadas mediante el simbolo '/'
# Esto nos vividira el string en una lista
primer.filtro <- strsplit(coordenadas, split='/')
# Recorremos cada coordenada, nos quedamos con los datos en formato numerico
# Creando un vector que tendra en primer lugar las latitudes y en el segundo
# Las longitudes
for( i in (1:length(coordenadas)) ) {
segundo.filtro[i] <- gsub( '[^0-9,;,.,-]', '', primer.filtro[[i]][3] )
coordenadas.filtradas[i] <- strsplit( segundo.filtro[i], ';' )
}
# Del vector de coordenadas, creamos vectores de latitud y longitud por separado
for( i in (1:length(coordenadas.filtradas))) {
latitud <- c( latitud, coordenadas.filtradas[[i]][1])
longitud <- c(longitud, coordenadas.filtradas[[i]][2])
}
# Agregamos cols con latitud y longitud a los datos de wikipedia
datos.wiki <- datos.wiki %>%
mutate(
latitud=latitud,
longitud=longitud
) %>%
filter(
IATA != ''
) %>%
select(
`City served`,
Province,
IATA,
`Airport name`,
latitud, longitud
) %>%
rename(
ciudad = `City served`,
provincia = Province,
Aeropuerto = `Airport name`
)
datos.wiki
Bueno, ahora tenemos los datos de la entrada de wikipedia, ya filtrados y listos para vincularlos con los datos emitidos por ANAC.
Lo primero que vamos a hacer acá es terminar de filtrar bien. Esto es, tomando los datos de ANAC y filtrando segun los codigos IATA que si tenemos de los datos de wikipedia.
En particlar, tambien nos quedaremos con los datos de calidad DEFINITIVO, teniendo en cuenta que los que estan como PROVISORIO quizas esten sometidos a actualizaciones y por tanto no son del todo confiables.
datos.vuelos.nacionales <- datos.vuelos.nacionales %>%
filter( Clasificacion_Vuelo %in% "Dom" &
Calidad.dato %in% "DEFINITIVO" &
Origen %in% codigos &
Destino %in% codigos
)
datos.vuelos.nacionales
Acá se nos plantea un problema… ¿Como es que inicialmente teniamos \(63597\) vuelo y ahora solo \(2712\)?
Bueno, aca tuvimos un pequeño problema con los codigos… .
Analisemos que codigos tiene cada uno de los datasets, esto es, los codigos en la pagina de wikipedia y en el csv de ANAC
codigos.ANAC <- datos.ANAC %>% select(Aeropuerto)
codigos.wiki <- datos.wiki %>% select(IATA)
table(codigos.ANAC)
## codigos.ANAC
## AER BAR BCA CAT CBA CHP CRR CRV DIA DIL DOZ DRY ECA
## 23083 7161 2122 982 6524 1903 3191 4633 566 91 7982 1676 1913
## ESQ EZE FDO FSA GAL GOY GPI GRA IGU JUA JUJ LAR MDP
## 791 22604 35395 511 2546 73 2 529 2212 1509 1720 15 2774
## MLG MOR NEU OSA PAL PAR POS ROS RTA RYD SAL SDE SIS
## 791 37031 8018 1062 2331 2409 3973 3567 2168 1880 4823 1944 1722
## SRA SVO TRC TRE TUC UIS USU VIE
## 3734 1542 1031 2015 3256 486 2675 837
codigos.ANAC <- names(table(codigos.ANAC))
cat("\n ------------ \n\n", codigos.ANAC ,"\n", length(codigos.ANAC),"\n ------------ \n\n")
##
## ------------
##
## AER BAR BCA CAT CBA CHP CRR CRV DIA DIL DOZ DRY ECA ESQ EZE FDO FSA GAL GOY GPI GRA IGU JUA JUJ LAR MDP MLG MOR NEU OSA PAL PAR POS ROS RTA RYD SAL SDE SIS SRA SVO TRC TRE TUC UIS USU VIE
## 47
## ------------
codigos.wiki <- names(table(codigos.wiki))
cat("\n ------------ \n\n", codigos.wiki, "\n ------------ \n\n")
##
## ------------
##
## AEP AFA AOL APZ ARR BHI BRC CCT CLX CNQ CNT COC COR CPC CPG CRD CRR CSZ CTC CUT CVH EHL ELO EMX EPA EQS EZE FMA FTE GGS GHU GNR GPO HOS IGB IGR ING IRJ JNI JSM JUJ LCM LCP LGS LHS LLS LMD LPG LUQ MCS MDQ MDX MDZ MJR MQD NCJ NEC NQN OES OLN ORA OVR OYA OYO PEH PMQ PMY PRA PRQ PSS PUD RAF RCQ RCU RDS REL RES RGA RGL RHD RLO ROS ROY RSA RYO RZA SDE SFN SGV SLA SST TDL TTG TUC UAQ ULA USH UZU VCF VDM VDR VGS VLG VME VMR
## ------------
cat("\n ------------ \n\n", length(codigos.wiki), "\n ------------ \n\n")
##
## ------------
##
## 105
## ------------
Veamos cuales coinciden.
# Funcion burbuja := realiza un "burbujeo" entre vectores.
# Esta, me permitira comparar los elementos coincidentes entre estos dos vectores.
burbuja <- function(vec.prim, vec.sec){
coinciden <- c()
for (i in (1:length(vec.prim))){
for (j in (1:length(vec.sec))){
if (vec.sec[j] == vec.prim[i]) {
coinciden <- c(coinciden, vec.prim[i])
}
}
}
return(coinciden)
}
burbuja(codigos.wiki, codigos.ANAC)
## [1] "CRR" "EZE" "JUJ" "ROS" "SDE" "TUC"
Vemos que solo coinciden 6 codigos, es decir, a lo que necesitamos no sirve… ¿Porque razón sera?
La razón es clara, y es el hecho de que los codigos en el dataset del ANAC no estan en S IATA sino, que estan justamente segun el ANAC, una lista de estos se pueden ver Acá
Por suerte, nos dieron otro dataset, los cuales tienen varios formatos de codigos y una de las columnas justamente son codigos ANAC para algunos de los aeropuertos.
Ahora, lo que quedaria es unir los datasets de wikipedia y de ANAC y reinterpretar un poco los datos para así luego poder calcular las distancias y las velocidades medias de cada vuelo.
data.nueva <- read.csv("sna_abril_2021_fixed_encoding.csv", encoding = 'utf-8')
data.nueva
Usando un bucle como el anterior, vamos a comparar que codigo de este nuevo dataset es el que coincide con los datos de ANAC
Solo compararemos los de la columna ita y ana, pues los otros codigos a simple vista son muy distintos.
codigos.ita <- names(table(data.nueva %>% select(ita)))
codigos.ana <- names(table(data.nueva %>% select(ana)))
cat("------------------------------",
"\n Comparación ita - ANAC \n",
"------------------------------\n",
burbuja(codigos.ANAC, codigos.ita ))
## ------------------------------
## Comparación ita - ANAC
## ------------------------------
## EZE FDO JUJ ROS SDE TUC
cat("\n------------------------------",
"\n Comparación ana - ANAC \n",
"------------------------------\n",
burbuja(codigos.ana, codigos.ANAC))
##
## ------------------------------
## Comparación ana - ANAC
## ------------------------------
## AER BAR BCA CAT CBA CHP CRR CRV DIA DIL DOZ DRY ECA ESQ EZE FDO FSA GAL GPI GRA IGU JUA JUJ LAR MDP MLG NEU OSA PAL PAR POS ROS RTA RYD SAL SDE SIS SRA SVO TRC TRE TUC UIS USU VIE
Listo, sabemos que la lista de codigos que debemos utilizar para comparar es la columna ana.
Tomemos de este dataset los datos que nos sean de utilidad, para luego mergear ambos datasets.
datos.aeropuertos.nacionales <- data.nueva %>%
select(
cpr, nam, fna, ana, x, y
) %>%
rename(
provincia = cpr,
ciudad = nam,
aeropuerto = fna,
codigo.ANAC = ana,
longitud = x,
latitud = y
)
datos.aeropuertos.nacionales
Ahora si, hagamos una union de datos entre los dataset de ANAC y los datos de aeropuertos.
new.df.vuelos.nacionales <- datos.ANAC %>%
filter(
Clasificacion_Vuelo == "Dom",
Clase_Vuelo == "Regular"
)
new.df.vuelos.nacionales<- new.df.vuelos.nacionales %>%
mutate(Origen =
if_else(Tipo.de.Movimiento=='Despegue',Aeropuerto,Origen...Destino),
Destino =
if_else(Tipo.de.Movimiento=='Despegue',Origen...Destino,Aeropuerto)
) %>%
filter(
Calidad.dato %in% "DEFINITIVO" &
Origen %in% codigos.ana &
Destino %in% codigos.ana
)
new.df.vuelos.nacionales
Esto tiene otra cara, ahora tenemos \(45024\) datos para analizar.
# Unimos los datasets utilizando la funcion merge
# Utililizando los datos del Origen
df_origen <- merge( x=new.df.vuelos.nacionales,
y=datos.aeropuertos.nacionales,
by.x='Origen', by.y='codigo.ANAC')
# Utilizando los datos del Destino
df_destino <- merge(x=new.df.vuelos.nacionales,
y=datos.aeropuertos.nacionales,
by.x='Destino', by.y='codigo.ANAC')
#ordeno a partir de fecha y hora (y si coinciden a partir del modelo de avion)
new.df.vuelos.nacionales <- new.df.vuelos.nacionales %>%
arrange( Fecha,
Hora.UTC,
Aeronave
)
# Realizamos el mismo ordenamiento con los datos mergeados
df_destino <- df_destino %>% arrange( Fecha, Hora.UTC, Aeronave )
df_origen <- df_origen %>% arrange( Fecha, Hora.UTC, Aeronave )
#agrego las columnas a los datos filtrados
new.df.vuelos.nacionales <- new.df.vuelos.nacionales %>%
mutate(
ciudad.origen = df_origen$ciudad,
ciudad.destino = df_destino$ciudad,
provincia.origen = df_origen$provincia,
provincia.destino = df_destino$provincia,
lat.origen = df_origen$latitud,
lat.destino = df_destino$latitud,
long.origen = df_origen$longitud,
long.destino = df_destino$longitud
)
new.df.vuelos.nacionales
Obtuvimos asi, un dataset que tiene las las coordenadas de origenes y destinos, ciudades de origen y destino y provincias.
Podemos notar tambien, que hay columnas que ya no son de utilidad. Por lo que crearemos teniendo en cuenta que:
Calidad de datos : ya no es nesesariaAeropuerto : sera reemplazado por el dato de OrigenOrigen...Destino : Se reemplazara por el DestinoLatitudes/origenes/pasajeros : Haremos la conversión de string a doublenew.df.vuelos.nacionales <- new.df.vuelos.nacionales %>%
select(
Fecha, Hora.UTC, Pasajeros, Clase_Vuelo,
Tipo.de.Movimiento, Aerolinea.Nombre,
Aeronave, Origen, Destino,
ciudad.origen, ciudad.destino,
provincia.origen, provincia.destino,
lat.origen, long.origen, lat.destino, long.destino,
) %>%
mutate(
Pasajeros = as.double(Pasajeros),
lat.origen = as.double(lat.origen),
long.origen = as.double(long.origen),
lat.destino = as.double(lat.destino),
long.destino = as.double(long.destino),
) %>%
rename(
Aerolinea = Aerolinea.Nombre
)
new.df.vuelos.nacionales
Para el calcular las distancias utilizaremos la libreria geosphere, la cual tiene una seriae de herramientas muy utiles para hacer calculos con trigonometria esferica.
Esta tiene una funcion llamada disHavestine (Cuyos detalles se pueden ver Acá), la cual nos permitira calcular las distancias a partir de los datos de las latitudes y las longitudes.
Esta función toma dos parametros inicialmente, los cuales son dos vectores cada uno tiene las longitudes y latitudes, “desde-hasta” para asi calcular la distancia.
require(geosphere)
## Loading required package: geosphere
# Iniciamos algunas variables
distancias <- c()
latitudes.origenes <- new.df.vuelos.nacionales$lat.origen
longitudes.origenes <- new.df.vuelos.nacionales$long.origen
latitudes.destino <- new.df.vuelos.nacionales$lat.destino
longitudes.destino <- new.df.vuelos.nacionales$long.destino
# Calculamos las distancias utilizando disHaversine
# Pasandolas a Kilometros
for (i in 1:45024){
distancias <- c( distancias,
(distHaversine(
c(latitudes.origenes[i],longitudes.origenes[i]),
c(latitudes.destino[i],longitudes.destino[i]))
)/1000)
}
distancias[1:3]
## [1] 1388.9893 1655.5274 929.2051
new.df.vuelos.nacionales <- new.df.vuelos.nacionales %>%
mutate(distancia.km = distancias)
new.df.vuelos.nacionales
Ya tenemos las distancias en Kilometros, calculadas en base a las coordenadas.
Así como esta el dato de las horas, no podemos utilizarlos para calcular las velocidades. Por suerte, estan en formato string y es facil reemplazar los : por ..
Por otro lado, tenemos hora de despegue o aterrizaje por lo que debemos identificar que el vuelo sea el mismo para saber cuando despego en un lugar (Horario de partida) y aterrizo en otro (Horario de llegada).
Algo a notar, es que tenemos filas impares, por lo que, al parecer hay más despegues que aterrizajes o al revez.
Veamos esto.
aterrizajes <- new.df.vuelos.nacionales %>% filter(Tipo.de.Movimiento == "Aterrizaje")
despegues <- new.df.vuelos.nacionales %>% filter(Tipo.de.Movimiento == "Despegue")
dim(aterrizajes)
## [1] 22126 18
dim(despegues)
## [1] 22898 18
Segun esto, tenemos mas despegues que aterrizajes… Esto es extraño, pero deberiamos quedarnos con un despegue por aterrizaje.
Empecemos con lo mas sencillo, que es pasar las Horas a double.
# Tomamos los datos de las horas
horas <- new.df.vuelos.nacionales %>%
select(Hora.UTC) %>%
mutate(numerico = as.double(gsub(":", ".", Hora.UTC)))
horas
Notemos, que de esta manera nos queda el tiempo en horas, pero no del todo, pues los decimales me estan indicando la cantidad de minutos y no la cantidad de horas en decimal.
Una de las cosas que podemos hacer es, dividir por el punto horas de minutos. Luego, pasamos las horas a minutos y a estos les sumamos los minutos restantes y luego pasamos estos minutos a horas.
Hagamos esto.
hs <- c()
min <- c()
hs.decimal <- c()
hs_string <- strsplit(new.df.vuelos.nacionales$Hora.UTC, ":")
for (i in (1:length(hs_string))){
hs[i] <- as.double(hs_string[[i]][1])
min[i] <- as.double(hs_string[[i]][2])
hs.decimal[i] <- ( (hs[i] * 60) + min[i] )/60
}
hs.decimal[1:10]
## [1] 0.4000000 0.4333333 0.4833333 0.6166667 7.1500000 8.1666667 8.2333333
## [8] 8.5833333 9.0666667 9.1833333
Luego, la variable hs.decimal tiene las horas de salida o llegada pasadas a decimales.
Sumamos estas al dataset con el que trabajabamos.
new.df.vuelos.nacionales <- new.df.vuelos.nacionales %>%
mutate(horario.num = hs.decimal)
new.df.vuelos.nacionales
Lo que nos faltaria es identificar que despegue coincide con que aterrizaje
datos.ANAC := datos del csv original de aterrizajes y vuelos.
datos.vuelos.nacionales := datos del csv de ANAC correspondiente a los vuelos nacionales.
datos.wiki := datos correspondientes a la tabla de wikipedia (sin ningun filtro).
codigos := Codigos IATA que se encuentran en la tabla de wikipedia.